操作系统概念学习笔记 第十七章 分布式协调

事件排序:

  决定整体排序的能力在许多应用中非常关键,因此提出一种分布式算法来将事前关系扩充为系统中的所有事件的一致性整体排序

  事前关系:

    在一组事件(假设发送和接收一条消息组成一个事件)上定义事前关系(用→表示)

      1 如果A和B是同一进程中的事件,并且A在B之前执行,则A→B

      2 如果A是某个进程中发送消息的事件,B是另一进程中接收此消息的事件,则A→B

      3 如果A→B,且B→C,则A→C

  实现:

    将每个系统事件与一个时间戳相联系,然后可以定义全局排序的必要条件。

 

 

 

 

互斥:

  集中式算法:

    系统中的某个进程被选为进入临界区的协调者,每个想调用互斥的进程发送一条请求消息给协调者,当此进程接收到一条来自于协调者地应答消息后,它就可以开始进入它的临界区。在退出它的临界区后,该进程发送一条释放消息给协调者并继续进行下去。

  完全分布式的算法:

    当进程p想进入临界区,它产生一个新的时间戳,并发送一条请求消息给系统中的所有其他进程,当这些进程接收到请求消息时,它们可能马上回复(即发回一条应答消息给p),或者推迟发送一个应答消息(因为它自己已经在其临界区中),如果这个进程接收到系统中所有其他进程的应答消息,则它可以进入临界区,并对到来的请求进行排队且延迟它们。在退出临界区后,该进程向所有被它延迟的请求发送应答消息。

    进程p是否立即回应一条请求消息,基于如下三个因素:

      1 如果进程p已经在它的临界区,则它推迟对其他进程的应答

      2 如果进程不想进入它的临界区,则它立即发送应答

      3 如果进程想进入它的临界区却还未进入,则它比较自己的请求时间戳和进程j产生的请求时间戳,如果自己的请求时间戳大,则它马上发送一条应答消息给j,否则应答被推迟

    该算法体现了下面的特征:

      1 获得了互斥

      2 保证了从死锁中获得自由

      3 由于进入临界区是根据时间戳排序,保证了从饥饿中得到解放,时间戳排序保证进程按FCFS次序服务。

      4 每次进入临界区所发送消息的数量是2*(n-1),这个数量是当各进程独立且并发地执行时,每次进入临界区所需消息的最小值

    由于此方法需要系统中所有进程参与,它产生三个额外后果

      1 进程需要知道系统中所有其他进程的标识,当一个新的进程加入到参与互斥算法的进程组中时,必须采取如下措施:

        a 进程必须接收组中所有其他进程的进程名

        b 新的进程名必须散布到组内的所有其他进程中

      2 如果一个进程出错,整个算法就会崩溃。可以通过连续监控系统中所有进程的状态来解决此问题。如果一个进程出错,则所有其他进程都被通知到,以使它们不再发送请求消息给出错的进程,当一个进程恢复后,它必须启动允许它重新加入进程组的程序。

      3 未进入其临界区的进程必须经常暂停,以保证其他想进入临界区的进程。因此该协议适合一组小的、稳定合作的进程。

  令牌传递算法:

    令牌是在系统中传递的一种特殊的消息,只有令牌的持有者才有权进入临界区。只有一个令牌,只有一个进程能进入临界区。

    系统中的进程被逻辑地组织成一个环结构,

    两种类型的错误:

      1 如果令牌丢失,必须通过一次选举来产生新的令牌

      2 如果一个进程出错,必须建立一个新的逻辑环

 

 

 

原子性:

  分布式系统中的事务协调者的作用在于保证分布式系统中事务执行的原子性。每个站点都有它自己的本地事务协调者,负责协调所有始于该站点的事务的执行。对每一件这样的事务,协调者负责如下工作:

    1 启动事务的执行

    2 将事务分成若干子事务,并将这些子事务分布到合适的站点去执行。

    3 协调事务的结束,它可能导致事务被提交到所有的站点,或在所有站点终止

  假设每个站点维护一个恢复用的日志

  两阶段提交协议:

    假设T是站点S发起的一个事务,并假设站点S的协调者为C

    第一阶段:

      C将记录<perpare T>加到日志中,并将记录存入稳定的存储器中,然后发送一条prepare(T)消息给所有执行T的站点。

      在接收到这样一条消息后,站点上的事务管理者决定是否提交它的T部分。

      如果回答是"不",添加一条<no T>记录到日志中,然后通过发送一条abort(T)消息给C来做出反应

      如果回答是“是”,则添加一条<ready T>记录到日志中,然后它将所有符合T的日志记录存入稳定的存储器中

      事务管理者用一条ready(T)消息回答C

    第二阶段:

      当C接收到所有其他站点对其所发消息prepare(T)的响应时,或者当消息被送出后已有一段预先指定的时间间隔流逝时,C就可以决定是否提交或终止事务T。

      如果C从所有参与的站点处接收到ready(T)消息,则事务T可被提交,否则事务T必须被终止

      根据此裁决,或者在日志中加入记录<commit T>,或者加入记录<abort T>,且被强制写入稳定存储器上

      此时,事务的命运是未知的,

      随后,协调者或者发送消息<commit T>,或者发送消息<abort>给所有参与的站点。

      当一个站点接收到此消息后,它将消息记录到日志中

    执行T的一个站点在它发送ready(T)消息给协调者之前的任何时候都可以无条件地终止T。

    ready(T)消息实际上是一个站点许诺以遵循协调者的命令来提交T或终止T

    一个站点能做出此承诺的唯一情形是所需的信息已存入到稳定的存储器中。

    否则,如果站点在发送T准备好之后崩溃,它就不可能实现它的承诺

    在某些2PC协议的实现方式中,一个站点在两阶段协议的最后发送一条应答T消息给协调者,当协调者接收到所有站点的应答T消息时,它将记录<complete T>加到日志中

  2PC中的错误处理:

    1 一个参与站点的出错

      当一个参与的站点从一次出错中恢复后,它必须检查它的日志以决定哪些当错误发生时正在执行的事务的命运

        * 日志包含一条<commit T>记录,这种情况下,站点执行redo(T)

        * 日志包含一条<abort T>记录,这种情况下,站点执行undo(T)

        * 日志包含一条<ready T>记录,这种情况下,站点必须查阅C来决定T的命运,如果C已准备好,它通知S是提交还是终止

        * 日志没有包含关于T的控制记录(终止、提交、准备),必须执行undo(T)

    2 协调者出错

    3 网络出错

 

 

 

 

并发控制:

  加锁协议:

    1 非复制方法:

      每个站点维持一个锁管理者,它的功能是管理对存储在站点中的数据的加锁和解锁请求。

      当一个事务希望在站点S对数据项Q加锁,它简单地发送一条消息给站点S的锁管理者请求加锁

      一旦锁管理者认为请求可以被批准,则发回一条消息给初始者,以表明加锁请求已被批准

      此方法具有易于实现的优点,但是死锁处理更为复杂

    2 单协调者方法:

      系统维护驻留在单个选定的站点S上的单个锁管理者。所有的加锁和解锁请求都在站点S上生成。

      当某事务需要对一数据项加锁时,它发送一个加锁请求给S,锁管理者决定是否同意立即加锁,

      如果同意,发回一条信息给加锁请求者,否则,请求被延迟,直到被同意,此时发送一条消息给加锁请求者。

      优点:

        易实现

        易进行死锁处理

      缺点:

        瓶颈:站点S称为瓶颈,因为所有请求都要在此处理

        脆弱性:如果站点S出错,使得并发控制丢失,要么必须停止处理,要么必须使用恢复方案

    3 多数协议:

      系统为每个站点维护一个锁管理者,每个管理者控制存储在站点上的所有数据或其拷贝的加锁。

      当某事务希望对一个在n个不同的站点上拥有拷贝的数据项Q加锁时,该事务必须对超过半数的站点发送一个加锁请求

      每个锁管理者决定是否能立即加锁,

      直到事务成功地获得对Q拷贝的多数加锁,它才在Q上进行操作。

      缺点:

        实现:多数协议实现起来复杂得多,它需要2(n/2+1)个消息来处理加锁请求,还要(n/2+1)个消息来处理解锁请求

        死锁处理:由于加锁和解锁请求不在一个站点上进行,必须修改死锁处理算法

    4 偏倚协议:

      偏倚协议基于类似于多数协议的模式。不同之处在于对共享锁的请求比对排他锁地请求得到了更便利的处理。

      系统在每个站点维护一个锁管理者,每个锁管理者管理所有存储在此站点数据项的锁,共享锁和排它锁以不同的方式处理:

        共享锁:当某事务需要对数据项Q加锁时,它简单地从一个包含Q拷贝的站点上的锁管理者那里请求对Q加锁

        排它锁:当一个事务需要对数据项Q加锁时,它从所有包含Q拷贝的站点上的锁管理者那里请求对Q加锁

    5 主拷贝

      在数据复制中,可以选择某个拷贝做为主拷贝。对于每个数据项Q,Q的主拷贝必须准确驻留在一个站点上,该站点被称为主站点

      当一个事务需要对一数据项Q加锁时,它请求在Q的主战点上加锁

  时间戳

    1 唯一时间戳的产生:

      集中式:选择一个站点来分派时间戳,可以使用逻辑计数器或它自己的时钟来完成任务

      分布式:每个站点利用逻辑计数器或时钟来生成一个唯一的本地时间戳,全局的唯一时间戳则通过将此本地唯一时间戳与站点标识相连接来获得,该站点标识也必须唯一。如果一个站点产生本地时间戳的速度比其他站点块,可以在事务通知时,对比本地时间戳和发来的时间戳,如果小,调快一下

    2 时间戳排序方法

 

 

 

 

死锁处理

  死锁预防:

    1 只需对系统资源简单地定义一个全局排序,就可以利用资源排序的死锁预防技术。整个系统中所有的资源都被赋予唯一的编号,只有当进程当前未占用编号大于j的资源时,才可以请求编号为j的资源。

    2 通过指定系统中的某个进程作为维护所需信息的进程来实现银行家算法,每个资源请求必须通过银行家引导。由于出入银行家的消息数量可能很大,故银行家可能成为瓶颈。所以此算法在分布式系统中可能不太实用

    3 基于资源抢占的时间戳排序方法,给每个进程赋予一个优先权号,此方法的一个问题在于可能产生饥饿,一些优先权特别低的进程可能永远被回滚,可以用时间戳来避免此问题发生。系统中的每个进程在它生成时被赋予了一个唯一的时间戳,下面提出了两个互相补充的使用时间戳的死锁预防方法:

      A 等待-死亡方法:该方法基于非抢占技术。当一个进程C请求一个正被D占用的资源时,只有当C的时间戳小于D时,允许C等待,否则C回滚(死亡)。例如:假设进程C、D、E的时间戳分别为5、10、15,如果C请求一个由D占有的资源,C将等待;如果E请求一个由D占用的资源,则E将被回滚。

      B 伤害-等待方法:该方法基于抢占技术,是等待-死亡系统的对等。当进程C请求一个正被D占用的资源时,只有当C的时间戳大于D时,允许C等待。否则,D回滚。回到上一个例子,在进程C、D、E中,如果C请求一个由D占有的资源,则D的资源被抢占,D回滚;如果E请求一个由D占有的资源,则E等待。

    假定一个进程被回滚,不再被赋予新的时间戳,则上面两种方法都能避免饥饿。  

    两种方法都存在的主要问题是可能发生不必要的回滚。

  死锁检测:

    死锁预测算法即使在没有死锁发生时也可能抢占资源。可以利用死锁检测算法,来避免不必要的抢占。

    1 集中式方法:

      全局资源图作为所有本地等待关系图的并集,由一个专门的进程维护:死锁检测协调者。由于系统中存在通信延迟,必须区分两种类型的资源图。真实图及时描述了在任何情况下真实但未知的系统状态,就好像一个无所不知的观察者所观察到的那样。构建图是在协调者地算法执行期间产生的一个近似。构建图必须保证不管何时调用检测算法,报告的结果都是正确的。所谓正确,意味着:

        a 如果存在一个死锁,它被正确地报告

        b 如果报告了死锁,系统事实上已处于死锁状态

      正如将要证明的,构造这样的一个正确的算法是不容易地。

      等待关系图可以在以下三个不同的时间及时得到构造:

        a 当从本地等待关系图中加入一条边或删除一条边时

        b 周期性地,当在一个等待关系图中发生了一些变化时

        c 当死锁检测协调者需要调用回路检测算法时。

 

 

 

 

 

选举算法:

  许多分布式算法使用一个协调者进程来完成系统中其他进程所需的功能。如果协调者由于它所在站点的出错而出现错误,只需通过重新启动其他站点上一个新的协调者备份,系统就可以继续执行。决定何处可以重新启动协调者的新备份的算法称为选举算法。

    Bully算法:

      假设进程i发送一个请求,它在一个时间间隔T内未被协调者响应。此时,假设协调者出现错误,并且i试图选举自己作为新的协调者,该任务通过下面的算法来完成。

      进程i向所有具有更高程号的进程发送一条选举消息,然后在时间间隔T内等待这些进程的响应

      如果在T时间间隔内没有收到任何响应,i就假定所有进程号高于i的进程出现错误,并选自己作为新的协调者。

      进程i重新启动一个新的协调者备份,并发送一条消息告知所有优先权号小于i的活动进程,现在i是协调者

      如果收到了回答,i在一个时间间隔t内,等待接收一条消息,并告诉它一个具有更高优先权号地进程已经被选举

      如果在时间t内没有发送消息,那么就假定具有更高优先权号地进程出现错误,进程i应重新启动算法。

      完成此算法的进程具有最高的进程号,并被选为协调者,它将它的进程号发送给其他所有进程号比它小的活动进程。

      一个出错的进程恢复之后,它马上开始执行同样的算法,如果没有更高进程号的活动进程,即使现在有一个进程号比它小的

      活动协调者,恢复的进程也强迫所有其他进程号小于它的进程让它成为协调者进程。

      例子:包括1到4进程:

        1 所有进程都是活动的,4是协调者进程

        2 1和4出现错误,2通过发送一个请求,并在时间T内未收到回答来检测到4出现错误,然后2通过发送一个请求给3来开始它自己的选举算法

        3 3收到请求,并响应2,通过发送一个请求给4来开始它自己的选举算法

        4 2收到3的响应,开始等待一个时间间隔t

        5 4在时间T内未响应,故3选举它自己为新的协调者,并发送进程号3给1和2(其中1由于出现错误而未接收到)

        6 接着,当1恢复后,它发送一条选举请求给2,3,4

        7 2和3响应1,并开始它们自己的选举算法,根据前面的事件,3将再次被选举

        8 最后,4恢复并通知1、2、3,它是当前的协调者,(由于4最大,所以它发出不选举的请求)

  环算法

    环算法假设连接是无方向的,并且进程将消息发送给它们右边的邻居。

    算法所用的主要数据结构是活动列表,它包含算法结束时系统中所有活动进程的优先权号,每个进程维护它自己的活动列表

    该算法工作如下:

      1 如果进程i检测到一个协调者出错,它生成一个新的初始值为空的活动列表,然后发送一条消息e给它右边的邻居,并将进程号i加到它的活动列表中

      2 如果进程j从它左边的进程收到一条消息e,它必须用如下三种方式之一进行反应:

        a 如果这是它见到或发送的第一条e消息,j生成一个具有i和j的新活动列表,然后发送消息i,后面紧跟消息j

        b 如果i!=j,既接收的消息不包含i的进程号,则j将j加到它的活动列表中,并将消息转发到它的右邻居

        c 如果i=j,即i的活动列表包含系统中所有活动进程的进程号,进程i现在就能确定活动列表中最大的进程号,并用此标识新的协调者进程。

 

 

 

达成一致:

  为了使系统可靠,需要一种机制以允许一组进程在某个公共值上达成一致。这样的一致可能由于几个原因而不会发生:

    1 通信介质可能出错,导致消息丢失或垃圾消息

    2 进程自己可能出错,导致不可预知的进程行为

  不可靠通信:

  故障处理:

 

posted @ 2013-10-12 23:33  褐色键盘  阅读(413)  评论(0编辑  收藏  举报